﻿using Naruto.Subscribe;
using Naruto.Subscribe.Extension;
using Naruto.Subscribe.Interface;
using Naruto.Subscribe.Object;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Naruto.Subscribe.Internal
{
    internal interface IDynamicMethodExpression : IDisposable
    {
        /// <summary>
        /// 执行动态方法
        /// </summary>
        /// <param name="service">服务</param>
        /// <param name="action">方法名</param>
        /// <param name="parameterEntities">参数</param>
        /// <param name="isAsync">方法是否为异步</param>
        /// <returns></returns>
        Task ExecAsync(object service, string action, bool isAsync, List<ParameterEntity> parameterEntities);
    }
    /// <summary>
    /// 张海波
    /// 2020-09-05
    /// 执行对应方法的表达式目录树
    /// </summary>
    internal class DynamicMethodExpression<SubscribeType> : IDynamicMethodExpression where SubscribeType : ISubscribe
    {
        private readonly IMethodCache methodCache;

        /// <summary>
        /// 存储委托
        /// </summary>
        private static ConcurrentDictionary<string, Delegate> exec;

        public DynamicMethodExpression(IMethodCache _methodCache)
        {
            exec = new ConcurrentDictionary<string, Delegate>();
            methodCache = _methodCache;
        }

        /// <summary>
        /// 执行方法
        /// </summary>
        /// <param name="service"></param>
        /// <param name="action">执行的方法</param>
        /// <param name="parameterEntities">方法的参数</param>
        /// <returns></returns>
        public async Task ExecAsync(object service, string action, bool isAsync, List<ParameterEntity> parameterEntities)
        {
            service.CheckNull();
            action.CheckNullOrEmpty();
            //从缓存中取
            if (!exec.TryGetValue(service.GetType().Name + action, out var execAction))
            {
                execAction = Create(service, action, parameterEntities);
            }
            if (isAsync)
                await (execAction.DynamicInvoke(BuildParameterService(parameterEntities, service)) as Task);
            else
                execAction.DynamicInvoke(BuildParameterService(parameterEntities, service));
        }


        /// <summary>
        /// 创建委托
        /// </summary>
        /// <param name="service">继承NarutoWebSocketService的服务</param>
        /// <param name="action">执行的方法</param>
        /// <param name="parameterEntities">参数</param>
        /// <returns></returns>
        private Delegate Create(object service, string action, List<ParameterEntity> parameterEntities)
        {
            //定义输入参数
            var p1 = Expression.Parameter(service.GetType(), "service");

            //方法的参数对象
            var methodParameter = new List<ParameterExpression>();
            foreach (var item in parameterEntities)
            {
                methodParameter.Add(Expression.Parameter(item.ParameterType, item.ParameterType.Name));
            }

            //动态执行方法
            var methodCacheModel = methodCache.Get(new BaseSubscribeTypeModel
            {
                ServiceType = service.GetType(),
                MethodName = action
            });
            if (methodCacheModel == null)
            {
                return default;
            }
            //调用指定的方法
            MethodCallExpression actionCall = null;
            //验证是否方法是否 有参数
            if (parameterEntities == null || parameterEntities.Count() <= 0)
            {
                //执行无参方法
                actionCall = Expression.Call(p1, methodCacheModel.Method);
            }
            else
            {
                //执行有参的方法
                actionCall = Expression.Call(p1, methodCacheModel.Method, methodParameter);
            }
            //参数信息
            var parameterList = new List<ParameterExpression>(methodParameter.Count + 1)
            {
                p1
            };
            parameterList.AddRange(methodParameter);
            //生成lambda
            var lambda = Expression.Lambda(actionCall, parameterList);
            //获取key
            var key = service.GetType().Name + action;
            //存储
            exec.TryAdd(key, lambda.Compile());

            return exec[key];
        }

        /// <summary>
        /// 构建参数
        /// </summary>
        /// <param name="parameterEntities"></param>
        /// <returns></returns>
        private object[] BuildParameter(List<ParameterEntity> parameterEntities)
        {
            return parameterEntities?.Select(a => a.Value).ToArray();
        }

        /// <summary>
        /// 构建参数
        /// </summary>
        /// <param name="parameterEntities"></param>
        /// <returns></returns>
        private object[] BuildParameterService(List<ParameterEntity> parameterEntities, object service)
        {
            //获取参数
            var parameters = BuildParameter(parameterEntities);
            //定义数组长度
            var length = (parameters == null ? 0 : parameters.Length) + 1;
            //初始化 结果的数组信息
            var result = new object[length];
            result[0] = service;

            for (int i = 1; i <= parameters.Length; i++)
            {
                result[i] = parameters[i - 1];
            }

            return result;
        }
        public void Dispose()
        {
            Dispose(true);
        }
        protected virtual void Dispose(bool isDispose)
        {
            if (isDispose)
            {
                exec?.Clear();
                GC.SuppressFinalize(this);
            }
        }
    }
}
